local E, L, V, P, G = unpack(ElvUI); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
local AB = E:GetModule("ActionBars");

--Cache global variables
--Lua functions
local _G = _G
local find, format, upper, sub = string.find, string.format, string.upper, string.sub
local floor = math.floor
local select, tonumber, pairs, getn = select, tonumber, pairs, getn
--WoW API / Variables
local CreateFrame = CreateFrame
local EnumerateFrames = EnumerateFrames
local GameTooltip_Hide = GameTooltip_Hide
local GetBindingKey = GetBindingKey
local GetCurrentBindingSet = GetCurrentBindingSet
local GetMacroInfo = GetMacroInfo
local IsAddOnLoaded = IsAddOnLoaded
local IsAltKeyDown = IsAltKeyDown
local IsControlKeyDown = IsControlKeyDown
local IsShiftKeyDown = IsShiftKeyDown
local LoadBindings, SaveBindings = LoadBindings, SaveBindings
local SetBinding = SetBinding
local CHARACTER_SPECIFIC_KEYBINDING_TOOLTIP = CHARACTER_SPECIFIC_KEYBINDING_TOOLTIP
local CHARACTER_SPECIFIC_KEYBINDINGS = CHARACTER_SPECIFIC_KEYBINDINGS

local bind = CreateFrame("Frame", "ElvUI_KeyBinder", E.UIParent)

function AB:ActivateBindMode()
	bind.active = true
	E:StaticPopupSpecial_Show(ElvUIBindPopupWindow)
	AB:RegisterEvent("PLAYER_REGEN_DISABLED", "DeactivateBindMode", false)
end

function AB:DeactivateBindMode(save)
	if save then
		SaveBindings(GetCurrentBindingSet())
		E:Print(L["Binds Saved"])
	else
		LoadBindings(GetCurrentBindingSet())
		E:Print(L["Binds Discarded"])
	end
	bind.active = false
	self:BindHide()
	self:UnregisterEvent("PLAYER_REGEN_DISABLED")
	E:StaticPopupSpecial_Hide(ElvUIBindPopupWindow)
	AB.bindingsChanged = false
end

function AB:BindHide()
	bind:ClearAllPoints()
	bind:Hide()
	GameTooltip:Hide()
end

function AB:BindListener(key)
	AB.bindingsChanged = true
	if key == "ESCAPE" or key == "RightButton" then
		if bind.button.bindings then
			for i = 1, getn(bind.button.bindings) do
				SetBinding(bind.button.bindings[i])
			end
		end
		E:Print(format(L["All keybindings cleared for |cff00ff00%s|r."], bind.button.name))
		self:BindUpdate(bind.button, bind.spellmacro)
		if bind.spellmacro ~= "MACRO" then GameTooltip:Hide() end
		return
	end

	if key == "LSHIFT"
	or key == "RSHIFT"
	or key == "LCTRL"
	or key == "RCTRL"
	or key == "LALT"
	or key == "RALT"
	or key == "UNKNOWN"
	or key == "LeftButton"
	then return end

	if key == "MiddleButton" then key = "BUTTON3" end
	if find(key, "Button%d") then
		key = upper(key)
	end

	local alt = IsAltKeyDown() and "ALT-" or ""
	local ctrl = IsControlKeyDown() and "CTRL-" or ""
	local shift = IsShiftKeyDown() and "SHIFT-" or ""

	if not bind.spellmacro or bind.spellmacro == "PET" or bind.spellmacro == "SHAPESHIFT" then
		SetBinding(alt..ctrl..shift..key, bind.button.bindstring)
	else
		SetBinding(alt..ctrl..shift..key, bind.spellmacro.." "..bind.button.name)
	end
	E:Print(alt..ctrl..shift..key..L[" |cff00ff00bound to |r"]..bind.button.name..".")
	self:BindUpdate(bind.button, bind.spellmacro)
	if bind.spellmacro ~= "MACRO" then GameTooltip:Hide() end
end

function AB:BindUpdate(button, spellmacro)
	if not bind.active then return end

	bind.button = button
	bind.spellmacro = spellmacro

	bind:ClearAllPoints()
	bind:SetAllPoints(button)
	bind:Show()

	ShoppingTooltip1:Hide()

	if not bind:IsMouseEnabled() then
		bind:EnableMouse(true)
	end

	if spellmacro == "MACRO" then
		bind.button.id = bind.button:GetID()

		if floor(.5 + select(2, MacroFrameTab1Text:GetTextColor()) * 10) / 10 == .8 then bind.button.id = bind.button.id + MAX_MACROS end

		bind.button.name = GetMacroInfo(bind.button.id)

		GameTooltip:SetOwner(bind, "ANCHOR_TOP")
		GameTooltip:SetPoint("BOTTOM", bind, "TOP", 0, 1)
		GameTooltip:AddLine(bind.button.name, 1, 1, 1)

		bind.button.bindings = {GetBindingKey(spellmacro.." "..bind.button.name)}
		if getn(bind.button.bindings) == 0 then
			GameTooltip:AddLine(L["No bindings set."], .6, .6, .6)
		else
			GameTooltip:AddDoubleLine(L["Binding"], L["Key"], .6, .6, .6, .6, .6, .6)
			for i = 1, getn(bind.button.bindings) do
				GameTooltip:AddDoubleLine(L["Binding"]..i, bind.button.bindings[i], 1, 1, 1)
			end
		end
		GameTooltip:Show()
	elseif spellmacro == "SHAPESHIFT" or spellmacro == "PET" then
		bind.button.id = tonumber(button:GetID())
		bind.button.name = button:GetName()

		if not bind.button.name then return end

		if not bind.button.id or bind.button.id < 1 or bind.button.id > (spellmacro == "SHAPESHIFT" and 10 or 12) then
			bind.button.bindstring = "CLICK "..bind.button.name..":LeftButton"
		else
			bind.button.bindstring = (spellmacro == "SHAPESHIFT" and "SHAPESHIFTBUTTON" or "BONUSACTIONBUTTON")..bind.button.id
		end

		GameTooltip:AddLine(L["Trigger"])
		GameTooltip:Show()
		GameTooltip:SetScript("OnHide", function()
			this:SetOwner(bind, "ANCHOR_NONE")
			this:SetPoint("BOTTOM", bind, "TOP", 0, 1)
			this:AddLine(bind.button.name, 1, 1, 1)
			bind.button.bindings = {GetBindingKey(bind.button.bindstring)}
			if getn(bind.button.bindings) == 0 then
				this:AddLine(L["No bindings set."], .6, .6, .6)
			else
				this:AddDoubleLine(L["Binding"], L["Key"], .6, .6, .6, .6, .6, .6)
				for i = 1, getn(bind.button.bindings) do
					this:AddDoubleLine(i, bind.button.bindings[i])
				end
			end
			this:Show()
			this:SetScript("OnHide", nil)
		end)
	else
		bind.button.action = tonumber(button.action)
		bind.button.name = button:GetName()

		bind.button.bindstring = bind.button.buttonType
		if not bind.button.bindstring and (find(bind.button.name, "BonusActionButton") or find(bind.button.name, "ActionButton")) then
			bind.button.bindstring = "ACTIONBUTTON"
		end

		if tonumber(sub(bind.button.name, -2)) then
			bind.button.bindstring = bind.button.bindstring .. sub(bind.button.name, -2)
		elseif tonumber(sub(bind.button.name, -1)) then
			bind.button.bindstring = bind.button.bindstring .. sub(bind.button.name, -1)
		end

		GameTooltip:AddLine(L["Trigger"])
		GameTooltip:Show()
		GameTooltip:SetScript("OnHide", function()
			this:SetOwner(bind, "ANCHOR_TOP")
			E:Point(this, "BOTTOM", bind, "TOP", 0, 4)
			this:AddLine(bind.button.name, 1, 1, 1)
			bind.button.bindings = {GetBindingKey(bind.button.bindstring)}
			if getn(bind.button.bindings) == 0 then
				this:AddLine(L["No bindings set."], .6, .6, .6)
			else
				this:AddDoubleLine(L["Binding"], L["Key"], .6, .6, .6, .6, .6, .6)
				for i = 1, getn(bind.button.bindings) do
					this:AddDoubleLine(i, bind.button.bindings[i])
				end
			end
			this:Show()
			this:SetScript("OnHide", nil)
		end)
	end
end

local script
local shapeshift = ShapeshiftButton1:GetScript("OnClick")
local pet = PetActionButton1:GetScript("OnClick")

function AB:RegisterButton(b, override)
	if b.IsObjectType and b.GetScript and b:IsObjectType("CheckButton") then
		local buttonName = b:GetName()
		if buttonName then
			if find(buttonName, "MultiBarLeftButton")
			or find(buttonName, "MultiBarRightButton")
			or find(buttonName, "MultiBarBottomLeftButton")
			or find(buttonName, "MultiBarBottomRightButton")
			or find(buttonName, "BonusActionButton")
			or find(buttonName, "ActionButton") or override then
				HookScript(b, "OnEnter", function() self:BindUpdate(b) end)
				script = b:GetScript("OnClick")
				if script == shapeshift then
					HookScript(b, "OnEnter", function() self:BindUpdate(b, "SHAPESHIFT") end)
				elseif script == pet then
					HookScript(b, "OnEnter", function() self:BindUpdate(b, "PET") end)
				end
			end
		end
	end
end

function AB:RegisterMacro(addon)
	if addon == "Blizzard_MacroUI" then
		for i = 1, MAX_MACROS do
			local b = _G["MacroButton"..i]
			HookScript(b, "OnEnter", function() self:BindUpdate(b, "MACRO") end)
		end
	end
end

function AB:ChangeBindingProfile()
	if ElvUIBindPopupWindowCheckButton:GetChecked() then
		LoadBindings(2)
		SaveBindings(2)
	else
		LoadBindings(1)
		SaveBindings(1)
	end
end

function AB:LoadKeyBinder()
	bind:SetFrameStrata("DIALOG")
	bind:SetFrameLevel(99)
	bind:EnableMouse(true)
	bind:EnableKeyboard(true)
	bind:EnableMouseWheel(true)
	bind.texture = bind:CreateTexture()
	bind.texture:SetAllPoints(bind)
	bind.texture:SetTexture(0, 0, 0, .25)
	bind:Hide()

	bind:SetScript("OnEnter", function() local db = this.button:GetParent().db if db and db.mouseover then AB:Button_OnEnter(this.button) end end)
	bind:SetScript("OnLeave", function() AB:BindHide() local db = this.button:GetParent().db if db and db.mouseover then AB:Button_OnLeave(this.button) end end)
	bind:SetScript("OnKeyUp", function() self:BindListener(arg1) end)
	bind:SetScript("OnMouseUp", function() self:BindListener(arg1) end)
	bind:SetScript("OnMouseWheel", function() if arg1 > 0 then self:BindListener("MOUSEWHEELUP") else self:BindListener("MOUSEWHEELDOWN") end end)

	local b = EnumerateFrames()
	while b do
		self:RegisterButton(b)
		b = EnumerateFrames(b)
	end

	for b in pairs(self.handledButtons) do
		self:RegisterButton(b, true)
	end

	if not IsAddOnLoaded("Blizzard_MacroUI") then
		self:SecureHook("LoadAddOn", "RegisterMacro")
	else
		self:RegisterMacro("Blizzard_MacroUI")
	end

	--Special Popup
	local f = CreateFrame("Frame", "ElvUIBindPopupWindow", UIParent)
	f:SetFrameStrata("DIALOG")
	f:SetToplevel(true)
	f:EnableMouse(true)
	f:SetMovable(true)
	f:SetFrameLevel(99)
	f:SetClampedToScreen(true)
	f:SetWidth(360)
	f:SetHeight(130)
	E:SetTemplate(f, "Transparent")
	f:Hide()

	local header = CreateFrame("Button", nil, f)
	E:SetTemplate(header, "Default", true)
	header:SetWidth(100)
	header:SetHeight(25)
	header:SetPoint("CENTER", f, "TOP")
	header:SetFrameLevel(header:GetFrameLevel() + 2)
	header:EnableMouse(true)
	header:RegisterForClicks("LeftButtonUp", "RightButtonUp", "LeftButtonDown", "RightButtonDown")
	header:SetScript("OnMouseDown", function() f:StartMoving() end)
	header:SetScript("OnMouseUp", function() f:StopMovingOrSizing() end)

	local title = header:CreateFontString("OVERLAY")
	E:FontTemplate(title)
	title:SetPoint("CENTER", header, "CENTER")
	title:SetText("Key Binds")

	local desc = f:CreateFontString("ARTWORK")
	desc:SetFontObject("GameFontHighlight")
	desc:SetJustifyV("TOP")
	desc:SetJustifyH("LEFT")
	desc:SetPoint("TOPLEFT", 18, -32)
	desc:SetPoint("BOTTOMRIGHT", -18, 48)
	desc:SetText(L["Hover your mouse over any actionbutton or spellbook button to bind it. Press the escape key or right click to clear the current actionbutton's keybinding."])

	local perCharCheck = CreateFrame("CheckButton", f:GetName().."CheckButton", f, "OptionsCheckButtonTemplate")
	_G[perCharCheck:GetName().."Text"]:SetText(CHARACTER_SPECIFIC_KEYBINDINGS)

	perCharCheck:SetScript("OnShow", function()
		this:SetChecked(GetCurrentBindingSet() == 2)
	end)

	perCharCheck:SetScript("OnClick", function()
		if AB.bindingsChanged then
			E:StaticPopup_Show("CONFIRM_LOSE_BINDING_CHANGES")
		else
			AB:ChangeBindingProfile()
		end
	end)

	perCharCheck:SetScript("OnEnter", function()
		GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
		GameTooltip:SetText(CHARACTER_SPECIFIC_KEYBINDING_TOOLTIP, nil, nil, nil, nil, 1)
	end)

	perCharCheck:SetScript("OnLeave", GameTooltip_Hide)

	local save = CreateFrame("Button", f:GetName().."SaveButton", f, "OptionsButtonTemplate")
	_G[save:GetName().."Text"]:SetText(L["Save"])
	E:Width(save, 150)
	save:SetScript("OnClick", function()
		AB:DeactivateBindMode(true)
	end)

	local discard = CreateFrame("Button", f:GetName().."DiscardButton", f, "OptionsButtonTemplate")
	E:Width(discard, 150)
	_G[discard:GetName().."Text"]:SetText(L["Discard"])

	discard:SetScript("OnClick", function()
		AB:DeactivateBindMode(false)
	end)

	--position buttons
	perCharCheck:SetPoint("BOTTOMLEFT", discard, "TOPLEFT", 0, 2)
	save:SetPoint("BOTTOMRIGHT", -14, 10)
	discard:SetPoint("BOTTOMLEFT", 14, 10)

	local S = E:GetModule("Skins")
	S:HandleCheckBox(perCharCheck)
	S:HandleButton(save)
	S:HandleButton(discard)
end
